/*******************************************************************************
 * Copyright (c) 2003, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.architexa.org.eclipse.gef.internal.ui.rulers;

import com.architexa.org.eclipse.draw2d.Figure;
import com.architexa.org.eclipse.draw2d.IFigure;
import com.architexa.org.eclipse.draw2d.geometry.Point;
import com.architexa.org.eclipse.draw2d.geometry.Rectangle;
import com.architexa.org.eclipse.gef.EditPart;
import com.architexa.org.eclipse.gef.GraphicalEditPart;
import com.architexa.org.eclipse.gef.Request;
import com.architexa.org.eclipse.gef.SharedCursors;
import com.architexa.org.eclipse.gef.commands.Command;
import com.architexa.org.eclipse.gef.commands.UnexecutableCommand;
import com.architexa.org.eclipse.gef.editparts.ZoomManager;
import com.architexa.org.eclipse.gef.editpolicies.GraphicalEditPolicy;
import com.architexa.org.eclipse.gef.requests.ChangeBoundsRequest;

import java.util.Iterator;
import java.util.List;



/**
 * @author Pratik Shah
 */
public class DragGuidePolicy 
	extends GraphicalEditPolicy 
{

private List attachedEditParts = null;
private IFigure dummyGuideFigure, dummyLineFigure;
private boolean dragInProgress = false;

protected IFigure createDummyLineFigure() {
	return new Figure();
}

protected GuideFigure createDummyGuideFigure() {
	return new GuidePlaceHolder(getGuideEditPart().isHorizontal());
}

/*
 * Fix for Bug# 65885
 * If you undo guide creation while dragging that guide, it was leaving behind drag
 * feedback.  This was because by the time eraseSourceFeedback() was being called,
 * the guide edit part had been deactivated (and hence eraseSourceFeedback is never
 * called on this policy).  So we make sure that this policy cleans up when it is
 * deactivated.
 */
public void deactivate() {
	removeFeedback();	
	super.deactivate();
}

private void eraseAttachedPartsFeedback(Request request) {
	if (attachedEditParts != null) {
		ChangeBoundsRequest req = new ChangeBoundsRequest(request.getType());
		req.setEditParts(attachedEditParts);
		
		Iterator i = attachedEditParts.iterator();
		
		while (i.hasNext())
			((EditPart)i.next()).eraseSourceFeedback(req);
		attachedEditParts = null;
	}
}

public void eraseSourceFeedback(Request request) {
	getGuideEditPart().updateLocationOfFigures(getGuideEditPart().getZoomedPosition());
	getHostFigure().setVisible(true);
	getGuideEditPart().getGuideLineFigure().setVisible(true);
	removeFeedback();
	getGuideEditPart().setCurrentCursor(null);
	dragInProgress = false;
	
	eraseAttachedPartsFeedback(request);
}

private List getAttachedEditParts() {
	if (attachedEditParts == null)
		attachedEditParts = getGuideEditPart().getRulerProvider()
				.getAttachedEditParts(getHost().getModel(), 
				((RulerEditPart)getHost().getParent()).getDiagramViewer());
	return attachedEditParts;
}

public Command getCommand(Request request) {
	Command cmd;
	final ChangeBoundsRequest req = (ChangeBoundsRequest)request;
	if (isDeleteRequest(req)) {
		cmd = getGuideEditPart().getRulerProvider()
				.getDeleteGuideCommand(getHost().getModel());
	} else {
		int pDelta;
		if (getGuideEditPart().isHorizontal()) {
			pDelta = req.getMoveDelta().y;
		} else {
			pDelta = req.getMoveDelta().x;
		}
		if (isMoveValid(getGuideEditPart().getZoomedPosition() + pDelta)) {
			ZoomManager zoomManager = getGuideEditPart().getZoomManager();
			if (zoomManager != null) {
				pDelta = (int)Math.round(pDelta / zoomManager.getZoom());				
			}
			cmd = getGuideEditPart().getRulerProvider()
					.getMoveGuideCommand(getHost().getModel(), pDelta);			
		} else {
			cmd = UnexecutableCommand.INSTANCE;
		}
	}
	return cmd;
}

protected IFigure getDummyGuideFigure() {
	if (dummyGuideFigure == null) {
		dummyGuideFigure = createDummyGuideFigure();
	}
	return dummyGuideFigure;
}

protected IFigure getDummyLineFigure() {
	if (dummyLineFigure == null) {
		dummyLineFigure = createDummyLineFigure();
	}
	return dummyLineFigure;
}

protected GuideEditPart getGuideEditPart() {
	return (GuideEditPart)getHost();
}

protected boolean isDeleteRequest(ChangeBoundsRequest req) {
	int pos, max, min;
	if (getGuideEditPart().isHorizontal()) {
		pos = req.getLocation().x;
		Rectangle zone = getHostFigure().getBounds()
				.getExpanded(GuideEditPart.DELETE_THRESHOLD, 0);
		min = zone.x;
		max = min + zone.width;
	} else {
		pos = req.getLocation().y;
		Rectangle zone = getHostFigure().getBounds()
				.getExpanded(0, GuideEditPart.DELETE_THRESHOLD);
		min = zone.y;
		max = min + zone.height;
	}
	return pos < min || pos > max;	
}

protected boolean isMoveValid(int zoomedPosition) {
	boolean result = true;
	ZoomManager zoomManager = getGuideEditPart().getZoomManager();
	int position = zoomedPosition;
	if (zoomManager != null) {
		position = (int)Math.round(position / zoomManager.getZoom());
	}
	Iterator guides = getGuideEditPart().getRulerProvider().getGuides().iterator();
	while (guides.hasNext()) {
		Object guide = guides.next();
		if (guide != getGuideEditPart().getModel()) {
			int guidePos = getGuideEditPart().getRulerProvider().getGuidePosition(guide);
			if (Math.abs(guidePos - position) < GuideEditPart.MIN_DISTANCE_BW_GUIDES) {
				result = false;
				break;
			}			
		}
	}
	
	return result;
}

private void removeFeedback() {
	if (getDummyGuideFigure().getParent() != null) {
		getDummyGuideFigure().getParent().remove(getDummyGuideFigure());			
	}
	if (getDummyLineFigure().getParent() != null) {
		getDummyLineFigure().getParent().remove(getDummyLineFigure());
	}
}

private void showAttachedPartsFeedback(ChangeBoundsRequest request) {
	ChangeBoundsRequest req = new ChangeBoundsRequest(request.getType());
	req.setEditParts(getAttachedEditParts());
	
	if (getGuideEditPart().isHorizontal()) 
		req.setMoveDelta(new Point(0, request.getMoveDelta().y));
	else
		req.setMoveDelta(new Point(request.getMoveDelta().x, 0));
	
	Iterator i = getAttachedEditParts().iterator();
	
	while (i.hasNext())
		((EditPart)i.next()).showSourceFeedback(req);
}

public void showSourceFeedback(Request request) {
	if (!dragInProgress) {
		dragInProgress = true;
		// add the placeholder guide figure to the ruler
		getHostFigure().getParent().add(getDummyGuideFigure(), 0);
		((GraphicalEditPart)getHost().getParent()).setLayoutConstraint(
				getHost(), getDummyGuideFigure(), 
				new Integer(getGuideEditPart().getZoomedPosition()));
		getDummyGuideFigure().setBounds(getHostFigure().getBounds());
		// add the invisible placeholder line figure to the primary viewer
		getGuideEditPart().getGuideLayer().add(getDummyLineFigure(), 0);
		getGuideEditPart().getGuideLayer().setConstraint(getDummyLineFigure(), 
				new Boolean(getGuideEditPart().isHorizontal()));
		getDummyLineFigure().setBounds(getGuideEditPart().getGuideLineFigure()
				.getBounds());
		// move the guide being dragged to the last index so that it's drawn on
		// top of other guides
		List children = getHostFigure().getParent().getChildren();
		children.remove(getHostFigure());
		children.add(getHostFigure());
	}
	ChangeBoundsRequest req = (ChangeBoundsRequest)request;
	if (isDeleteRequest(req)) {
		getHostFigure().setVisible(false);
		getGuideEditPart().getGuideLineFigure().setVisible(false);
		getGuideEditPart().setCurrentCursor(SharedCursors.ARROW);
		eraseAttachedPartsFeedback(request);
	} else {
		int newPosition;
		if (getGuideEditPart().isHorizontal()) {
			newPosition = getGuideEditPart().getZoomedPosition() + req.getMoveDelta().y;
		} else {
			newPosition = getGuideEditPart().getZoomedPosition() + req.getMoveDelta().x;
		}
		getHostFigure().setVisible(true);
		getGuideEditPart().getGuideLineFigure().setVisible(true);
		if (isMoveValid(newPosition)) {
			getGuideEditPart().setCurrentCursor(null);		
			getGuideEditPart().updateLocationOfFigures(newPosition);
			showAttachedPartsFeedback(req);
		} else {
			getGuideEditPart().setCurrentCursor(SharedCursors.NO);
			getGuideEditPart().updateLocationOfFigures(
					getGuideEditPart().getZoomedPosition());
			eraseAttachedPartsFeedback(request);
		}
	}
}

public boolean understandsRequest(Request req) {
	return req.getType().equals(REQ_MOVE);
}
	
}
